"""
Configuration management for specification fixing.

Provides settings loading, validation, and environment management.
"""

import os
from pathlib import Path
from typing import Dict, Any, Optional, Union
import json

from ..utils.exceptions import ConfigurationError
from ..utils.logger import get_logger

logger = get_logger(__name__)


class Settings:
    """Configuration settings manager."""

    def __init__(self, config_file: Optional[str] = None):
        """Initialize settings from config file."""
        self.config_file = config_file or self._find_config_file()
        self._config_data = {}
        self._load_config()

    def _find_config_file(self) -> str:
        """Find configuration file in standard locations."""
        possible_locations = [
            Path.cwd() / "config.json",
            Path(__file__).parent.parent.parent.parent / "config.json",
            Path.home() / ".specification_fixer" / "config.json",
        ]

        for location in possible_locations:
            if location.exists():
                logger.debug(f"Found config file: {location}")
                return str(location)

        # Return default location if none found
        default_location = Path.cwd() / "config.json"
        logger.warning(f"No config file found, using default: {default_location}")
        return str(default_location)

    def _load_config(self) -> None:
        """Load configuration from file."""
        try:
            if Path(self.config_file).exists():
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    self._config_data = json.load(f)
                logger.info(f"Configuration loaded from: {self.config_file}")
            else:
                self._config_data = self._get_default_config()
                logger.info("Using default configuration")
        except Exception as e:
            logger.error(f"Failed to load configuration: {e}")
            raise ConfigurationError(f"Failed to load configuration: {e}")

    def _get_default_config(self) -> Dict[str, Any]:
        """Get default configuration."""
        return {
            "specification_fixer": {
                "version": "1.0.0",
                "settings": {
                    "analysis": {
                        "max_document_size_mb": 10,
                        "timeout_seconds": 30,
                        "enable_caching": True,
                        "cache_duration_minutes": 60
                    },
                    "validation": {
                        "strict_mode": True,
                        "require_all_criteria": True,
                        "min_completeness_score": 80
                    },
                    "output": {
                        "generate_reports": True,
                        "report_format": ["html", "json"],
                        "output_directory": "reports"
                    },
                    "logging": {
                        "level": "INFO",
                        "file": "logs/specification_fixer.log",
                        "max_file_size_mb": 10,
                        "backup_count": 5
                    }
                },
                "paths": {
                    "modules": "modules/specification_fixer",
                    "tests": "tests",
                    "fixtures": "tests/fixtures",
                    "reports": "reports",
                    "logs": "logs"
                }
            }
        }

    def get(self, key: str, default: Any = None) -> Any:
        """
        Get configuration value by key.

        Args:
            key: Configuration key (supports dot notation)
            default: Default value if key not found

        Returns:
            Configuration value
        """
        keys = key.split('.')
        value = self._config_data

        try:
            for k in keys:
                value = value[k]
            return value
        except (KeyError, TypeError):
            return default

    def set(self, key: str, value: Any) -> None:
        """
        Set configuration value by key.

        Args:
            key: Configuration key (supports dot notation)
            value: Value to set
        """
        keys = key.split('.')
        config = self._config_data

        for k in keys[:-1]:
            if k not in config:
                config[k] = {}
            config = config[k]

        config[keys[-1]] = value
        logger.debug(f"Configuration updated: {key} = {value}")

    def get_analysis_settings(self) -> Dict[str, Any]:
        """Get analysis-specific settings."""
        return self.get("specification_fixer.settings.analysis", {})

    def get_validation_settings(self) -> Dict[str, Any]:
        """Get validation-specific settings."""
        return self.get("specification_fixer.settings.validation", {})

    def get_output_settings(self) -> Dict[str, Any]:
        """Get output-specific settings."""
        return self.get("specification_fixer.settings.output", {})

    def get_logging_settings(self) -> Dict[str, Any]:
        """Get logging-specific settings."""
        return self.get("specification_fixer.settings.logging", {})

    def get_paths(self) -> Dict[str, str]:
        """Get path configurations."""
        base_path = Path.cwd()
        paths = self.get("specification_fixer.paths", {})

        # Convert relative paths to absolute
        for key, path in paths.items():
            if not Path(path).is_absolute():
                paths[key] = str(base_path / path)

        return paths

    def get_module_path(self) -> Path:
        """Get module directory path."""
        paths = self.get_paths()
        return Path(paths.get("modules", "modules/specification_fixer"))

    def get_reports_path(self) -> Path:
        """Get reports directory path."""
        paths = self.get_paths()
        reports_path = Path(paths.get("reports", "reports"))
        reports_path.mkdir(parents=True, exist_ok=True)
        return reports_path

    def get_logs_path(self) -> Path:
        """Get logs directory path."""
        paths = self.get_paths()
        logs_path = Path(paths.get("logs", "logs"))
        logs_path.mkdir(parents=True, exist_ok=True)
        return logs_path

    def get_fixtures_path(self) -> Path:
        """Get fixtures directory path."""
        paths = self.get_paths()
        fixtures_path = Path(paths.get("fixtures", "tests/fixtures"))
        fixtures_path.mkdir(parents=True, exist_ok=True)
        return fixtures_path

    def save(self, file_path: Optional[str] = None) -> None:
        """
        Save configuration to file.

        Args:
            file_path: Optional file path (uses current config file if not provided)
        """
        target_file = file_path or self.config_file

        try:
            target_path = Path(target_file)
            target_path.parent.mkdir(parents=True, exist_ok=True)

            with open(target_path, 'w', encoding='utf-8') as f:
                json.dump(self._config_data, f, ensure_ascii=False, indent=2)

            logger.info(f"Configuration saved to: {target_path}")

        except Exception as e:
            logger.error(f"Failed to save configuration: {e}")
            raise ConfigurationError(f"Failed to save configuration: {e}")

    def validate(self) -> bool:
        """Validate configuration settings."""
        try:
            # Check required sections
            required_sections = ["specification_fixer"]
            for section in required_sections:
                if section not in self._config_data:
                    raise ConfigurationError(f"Missing required section: {section}")

            # Check analysis settings
            analysis = self.get_analysis_settings()
            required_analysis_keys = ["max_document_size_mb", "timeout_seconds"]
            for key in required_analysis_keys:
                if key not in analysis:
                    raise ConfigurationError(f"Missing analysis setting: {key}")

            # Check path settings
            paths = self.get_paths()
            required_path_keys = ["modules", "reports", "logs"]
            for key in required_path_keys:
                if key not in paths:
                    raise ConfigurationError(f"Missing path setting: {key}")

            logger.info("Configuration validation passed")
            return True

        except Exception as e:
            logger.error(f"Configuration validation failed: {e}")
            raise ConfigurationError(f"Configuration validation failed: {e}")

    def update_from_env(self) -> None:
        """Update settings from environment variables."""
        env_mappings = {
            "SPECIFICATION_FIXER_LOG_LEVEL": "specification_fixer.settings.logging.level",
            "SPECIFICATION_FIXER_MAX_DOC_SIZE": "specification_fixer.settings.analysis.max_document_size_mb",
            "SPECIFICATION_FIXER_TIMEOUT": "specification_fixer.settings.analysis.timeout_seconds",
            "SPECIFICATION_FIXER_REPORTS_DIR": "specification_fixer.paths.reports",
            "SPECIFICATION_FIXER_LOGS_DIR": "specification_fixer.paths.logs",
        }

        for env_var, config_key in env_mappings.items():
            value = os.getenv(env_var)
            if value is not None:
                # Convert to appropriate type
                if "mb" in config_key.lower():
                    value = int(value)
                elif "timeout" in config_key.lower():
                    value = int(value)

                self.set(config_key, value)
                logger.debug(f"Updated {config_key} from environment: {value}")


# Global settings instance
_settings: Optional[Settings] = None


def get_settings() -> Settings:
    """Get global settings instance."""
    global _settings
    if _settings is None:
        _settings = Settings()
        _settings.update_from_env()
    return _settings


def reload_settings(config_file: Optional[str] = None) -> Settings:
    """Reload settings from file."""
    global _settings
    _settings = Settings(config_file)
    _settings.update_from_env()
    return _settings